local fsm = {}
fsm.runners = {}

local state_map = {}
state_map.__index = state_map
function state_map:enter() end
function state_map:exit() end

local state_machine = {}
state_machine.__index = state_machine
state_machine.state_maps = {}
state_machine.current_state = ""
function state_machine:change_state(state)
	if state == nil or state == "" then return end
	if self.state_maps == nil then return end
	
	if self.current_state == state then return end
	
	-- check is there any state before change
	-- if exist,try to excute exit function in before-state at first
	if self.current_state ~= nil and self.current_state ~= "" then
		local current = self.state_maps[self.current_state]
		if current ~= nil then
			if current.exit ~= nil then
				current:exit()
			end
		end
	end
	
	-- change to new state and try to excute the enter function
	self.current_state = state
	local new_state = self.state_maps[self.current_state]
	if new_state ~= nil then
		if new_state.enter ~= nil then
			new_state:enter()
		end
	end
end

function fsm:init(target,enum)
	if target == nil then return end
	if enum == nil then return end
	if #enum < 1 then return end
	
	local runner = setmetatable({},{__index = state_machine})
	if self.runners[target] == nil then
		self.runners[target] = {}
		self.runners[target][1] = runner
	else
		local index = #self.runners[target] + 1
		self.runners[target][index] = runner
	end
	
	for i = 1, #enum, 1 do
		runner.state_maps[enum[i]] = setmetatable({},{__index = state_map})
		if target["on_" .. enum[i] .. "_enter"] ~= nil then
			runner.state_maps[enum[i]].enter = target["on_" .. enum[i] .. "_enter"]
		end
		if target["on_" .. enum[i] .. "_exit"] ~= nil then
			runner.state_maps[enum[i]].exit = target["on_" .. enum[i] .. "_exit"]
		end
	end
	
	return runner
end

function fsm:remove(target)
	if self.runners == nil then return end
	if self.runners[target] == nil then return end
	self.runners[target] = nil
end

return fsm